package com.glSto.controller;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.*;

import javax.net.ssl.*;
import java.io.*;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.crypto.Cipher;

/**
 * 部门数据同步控制器
 * 提供单一接口实现完整的部门数据同步流程：
 * 1. 验证用户身份参数
 * 2. 获取OAuth访问令牌
 * 3. 分页拉取部门数据
 * 4. 同步数据到数据库（中间表和正式表）
 */
@RestController
@RequestMapping("/api/department")
public class AccessControlSystem_BM {
    // 日志记录器
    private static final Logger logger = LoggerFactory.getLogger(AccessControlSystem_BM.class);
    // Gson实例用于JSON序列化/反序列化
    private static final Gson gson = new GsonBuilder()
            .setDateFormat("yyyy-MM-dd HH:mm:ss")  // 支持空格分隔的日期格式
            .create();

    // 注入Spring管理的JdbcTemplate用于数据库操作
    @Autowired
    private JdbcTemplate jdbcTemplate;

    // ---------------------- 常量定义 ----------------------
    // 接口URL
    private static final String TOKEN_API_URL = "https://10.105.0.5:443/evo-apigw/evo-oauth/1.0.0/oauth/extend/token";
    private static final String DEPARTMENT_PAGE_API_URL = "https://10.105.0.5:443/evo-apigw/evo-brm/1.2.0/department/page";
    private static final String PUBLIC_KEY_API = "https://10.105.0.5:443/evo-apigw/evo-oauth/1.0.0/oauth/public-key";

    // OAuth认证参数
    private static final String FIXED_CLIENT_ID = "wuyanggao";
    private static final String FIXED_CLIENT_SECRET = "42bec152-8f04-476a-9aec-e7d616ff3cb3";
    private static final String FIXED_GRANT_TYPE = "password";
    private static final String OAUTH_BEARER_PREFIX = "Bearer ";

    // 分页参数
    private static final int FIXED_PAGE_SIZE = 100;
    private static final int DEFAULT_PAGE_NUM = 1;

    // ---------------------- HTTPS证书处理（跳过证书校验） ----------------------
    static {
        try {
            // 创建信任所有证书的TrustManager
            TrustManager[] trustAllCerts = new TrustManager[]{
                    new X509TrustManager() {
                        public X509Certificate[] getAcceptedIssuers() { return null; }
                        public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException {}
                        public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException {}
                    }
            };

            // 初始化SSL上下文
            SSLContext sc = SSLContext.getInstance("SSL");
            sc.init(null, trustAllCerts, new java.security.SecureRandom());
            HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());

            // 信任所有主机名
            HostnameVerifier allHostsValid = (hostname, session) -> true;
            HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
        } catch (Exception e) {
            logger.error("SSL初始化异常", e);
            throw new RuntimeException("SSL初始化失败，影响HTTPS请求", e);
        }
    }

    // ---------------------- 核心接口 ----------------------
    /**
     * 部门数据同步接口
     * 一次调用完成整个同步流程
     *
     * @param request 包含用户名和密码的请求对象
     * @return 同步结果响应
     */
    @PostMapping("/AccessControlSystem_BM")
    public ApiResponse syncAllDepartments(@RequestBody SyncRequest request) {
        try {
            // 1. 参数验证
            if (request == null || request.getUsername() == null || request.getPassword() == null) {
                return ApiResponse.error("用户名和密码不能为空");
            }

            // 2. 获取访问令牌
            logger.info("开始获取访问令牌...");
            TokenData tokenData = getOAuthAccessToken(request.getUsername(), request.getPassword());
            logger.info("访问令牌获取成功，有效期：{}秒", tokenData.getExpires_in());

            // 3. 同步所有部门数据
            logger.info("开始同步部门数据...");
            SyncResult result = syncDepartments(tokenData.getAccess_token());

            // 4. 构造响应数据（Java 8兼容写法）
            Map<String, Object> responseData = new HashMap<>();
            responseData.put("totalDepartments", result.getTotalCount());
            responseData.put("syncTime", System.currentTimeMillis());
            responseData.put("tokenExpiresIn", tokenData.getExpires_in());
            responseData.put("message", "部门数据同步完成");

            return ApiResponse.success(responseData);

        } catch (Exception e) {
            logger.error("部门数据同步失败", e);
            return ApiResponse.error("同步失败：" + e.getMessage());
        }
    }

    // ---------------------- 核心业务方法 ----------------------

    /**
     * 获取OAuth访问令牌
     *
     * @param username 用户名
     * @param plainPassword 明文密码
     * @return 令牌数据
     * @throws Exception 异常
     */
    private TokenData getOAuthAccessToken(String username, String plainPassword) throws Exception {
        // 1. 获取公钥
        String publicKeyResponse = sendHttpsGetRequest(PUBLIC_KEY_API);
        String publicKey = extractPublicKey(publicKeyResponse);
        if (publicKey == null) {
            throw new RuntimeException("获取公钥失败，无法加密密码");
        }

        // 2. 加密密码
        String encryptedPassword = encrypt(plainPassword, publicKey);
        logger.info("密码加密完成，加密后长度：{}", encryptedPassword.length());

        // 3. 调用Token接口
        TokenRequest tokenRequest = new TokenRequest(username, encryptedPassword, publicKey);
        String tokenResponseJson = sendHttpsPostWithOAuth(TOKEN_API_URL, tokenRequest, "NO_AUTH_NEEDED");
        TokenResponse tokenResponse = gson.fromJson(tokenResponseJson, TokenResponse.class);

        // 4. 校验响应
        if (tokenResponse == null || !tokenResponse.isSuccess() || tokenResponse.getData() == null) {
            String errMsg = String.format("获取token失败，错误码：%s，信息：%s",
                    (tokenResponse != null ? tokenResponse.getCode() : "未知"),
                    (tokenResponse != null ? tokenResponse.getErrMsg() : "无响应信息"));
            throw new RuntimeException(errMsg);
        }

        return tokenResponse.getData();
    }

    /**
     * 同步所有部门数据
     *
     * @param accessToken 访问令牌
     * @return 同步结果
     */
    private SyncResult syncDepartments(String accessToken) {
        List<Department> allDepartments = new ArrayList<>();
        int currentPage = DEFAULT_PAGE_NUM;
        int totalPage = 1;

        try {
            // 清空中间表
            deleteZJB();

            // 循环分页获取所有部门
            while (currentPage <= totalPage) {
                DepartmentPageData pageData = getSinglePageDepartment(accessToken, currentPage);
                totalPage = pageData.getTotalPage();

                if (pageData.getPageData() != null && !pageData.getPageData().isEmpty()) {
                    allDepartments.addAll(pageData.getPageData());
                    logger.info("已获取第{}页数据，累计{}条", currentPage, allDepartments.size());
                }

                currentPage++;
                // 添加延迟避免请求过于频繁
                Thread.sleep(200);
            }

            return new SyncResult(allDepartments.size(), true);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException("同步过程被中断", e);
        } catch (Exception e) {
            throw new RuntimeException("同步部门数据失败", e);
        }
    }

    /**
     * 获取单页部门数据并同步到数据库
     *
     * @param accessToken 访问令牌
     * @param pageNum 页码
     * @return 分页数据
     */
    private DepartmentPageData getSinglePageDepartment(String accessToken, int pageNum) {
        try {
            // 构造分页请求
            DepartmentPageRequest pageRequest = new DepartmentPageRequest(pageNum);
            String responseJson = sendHttpsPostWithOAuth(DEPARTMENT_PAGE_API_URL, pageRequest, accessToken);

            // 解析响应
            DepartmentPageResponse response = gson.fromJson(responseJson, DepartmentPageResponse.class);
            if (response == null || !response.isSuccess() || response.getData() == null) {
                String errMsg = String.format("第%d页部门数据获取失败，错误码：%s",
                        pageNum, response != null ? response.getCode() : "未知");
                throw new RuntimeException(errMsg);
            }

            DepartmentPageData pageData = response.getData();

            // 同步当前页数据到数据库
            if (pageData.getPageData() != null && !pageData.getPageData().isEmpty()) {
                for (Department dept : pageData.getPageData()) {
                    insertDepartment(dept);
                }
            }

            return pageData;
        } catch (Exception e) {
            throw new RuntimeException("获取第" + pageNum + "页部门数据失败", e);
        }
    }

    /**
     * 将部门数据插入数据库
     *
     * @param dept 部门对象
     */
    private void insertDepartment(Department dept) {
        try {
            // 插入中间表
            String zjbSql = "INSERT INTO MJXTJK_BM_ZJB (" +
                    "id, parentId, name, memo, sort, " +
                    "parentIds, departmentSn, domainId, create_time, update_time" +
                    ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, SYSDATE, SYSDATE)";

            jdbcTemplate.update(zjbSql,
                    dept.getId(),
                    dept.getParentId(),
                    dept.getName(),
                    dept.getMemo(),
                    dept.getSort(),
                    dept.getParentIds(),
                    dept.getDepartmentSn(),
                    dept.getDomainId());

            // 检查正式表是否存在，不存在则插入
            String checkSql = "SELECT COUNT(1) FROM MJXTJK_BM WHERE id = ?";
            Integer count = jdbcTemplate.queryForObject(checkSql, Integer.class, dept.getId());

            if (count == null || count == 0) {
                String bmSql = "INSERT INTO MJXTJK_BM (" +
                        "id, parentId, name, memo, sort, " +
                        "parentIds, departmentSn, domainId, create_time, update_time" +
                        ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, SYSDATE, SYSDATE)";

                jdbcTemplate.update(bmSql,
                        dept.getId(),
                        dept.getParentId(),
                        dept.getName(),
                        dept.getMemo(),
                        dept.getSort(),
                        dept.getParentIds(),
                        dept.getDepartmentSn(),
                        dept.getDomainId());
            }

        } catch (Exception e) {
            logger.error("插入部门数据失败，ID:{}", dept.getId(), e);
        }
    }

    /**
     * 删除中间表数据
     */
    private void deleteZJB() {
        try {
            jdbcTemplate.update("DELETE FROM MJXTJK_BM_ZJB");
            logger.info("中间表数据已清空");
        } catch (Exception e) {
            logger.error("清空中间表失败", e);
            throw new RuntimeException("清空中间表失败", e);
        }
    }

    // ---------------------- HTTP请求工具方法 ----------------------

    /**
     * 发送带OAuth认证的HTTPS POST请求
     *
     * @param apiUrl 接口URL
     * @param requestParam 请求参数对象
     * @param accessToken 访问令牌
     * @return 响应内容
     * @throws IOException IO异常
     */
    private String sendHttpsPostWithOAuth(String apiUrl, Object requestParam, String accessToken) throws IOException {
        StringBuilder response = new StringBuilder();
        HttpsURLConnection connection = null;
        try {
            URL url = new URL(apiUrl);
            connection = (HttpsURLConnection) url.openConnection();
            connection.setRequestMethod("POST");
            connection.setConnectTimeout(5000);
            connection.setReadTimeout(8000);
            connection.setDoOutput(true);
            connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
            connection.setRequestProperty("Accept", "application/json");
            connection.setRequestProperty("Authorization", OAUTH_BEARER_PREFIX + accessToken);

            // 发送请求体
            String rawJson = gson.toJson(requestParam);
            try (OutputStream os = connection.getOutputStream()) {
                os.write(rawJson.getBytes(StandardCharsets.UTF_8));
                os.flush();
            }

            // 处理响应
            int responseCode = connection.getResponseCode();
            try (BufferedReader br = new BufferedReader(
                    new InputStreamReader(
                            (responseCode >= 200 && responseCode < 300) ?
                                    connection.getInputStream() : connection.getErrorStream(),
                            StandardCharsets.UTF_8))) {

                String line;
                while ((line = br.readLine()) != null) {
                    response.append(line);
                }
            }

            if (responseCode >= 300) {
                throw new IOException("HTTP请求失败，响应码：" + responseCode + "，响应内容：" + response.toString());
            }

            return response.toString();
        } finally {
            if (connection != null) {
                connection.disconnect();
            }
        }
    }

    /**
     * 发送HTTPS GET请求
     *
     * @param apiUrl 接口URL
     * @return 响应内容
     * @throws IOException IO异常
     */
    private String sendHttpsGetRequest(String apiUrl) throws IOException {
        StringBuilder response = new StringBuilder();
        HttpsURLConnection connection = null;
        try {
            URL url = new URL(apiUrl);
            connection = (HttpsURLConnection) url.openConnection();
            connection.setRequestMethod("GET");
            connection.setConnectTimeout(5000);
            connection.setReadTimeout(5000);

            try (BufferedReader br = new BufferedReader(
                    new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) {

                String line;
                while ((line = br.readLine()) != null) {
                    response.append(line);
                }
            }

            return response.toString();
        } finally {
            if (connection != null) {
                connection.disconnect();
            }
        }
    }

    // ---------------------- 加密与工具方法 ----------------------

    /**
     * 从响应中提取公钥
     *
     * @param jsonResponse JSON响应
     * @return 公钥字符串
     */
    private String extractPublicKey(String jsonResponse) {
        try {
            PublicKeyResponse response = gson.fromJson(jsonResponse, PublicKeyResponse.class);
            if (response != null && response.isSuccess() && response.getData() != null) {
                return response.getData().getPublicKey();
            }
            return null;
        } catch (Exception e) {
            logger.error("解析公钥失败", e);
            return null;
        }
    }

    /**
     * 使用RSA公钥加密密码
     *
     * @param password 明文密码
     * @param publicKey 公钥
     * @return 加密后的密码
     * @throws Exception 加密异常
     */
    private String encrypt(String password, String publicKey) throws Exception {
        byte[] decodedPublicKey = java.util.Base64.getDecoder().decode(publicKey);
        RSAPublicKey rsaPublicKey = (RSAPublicKey) KeyFactory.getInstance("RSA")
                .generatePublic(new X509EncodedKeySpec(decodedPublicKey));

        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, rsaPublicKey);

        byte[] inputBytes = password.getBytes(StandardCharsets.UTF_8);
        int inputLength = inputBytes.length;
        int MAX_ENCRYPT_BLOCK = 117; // RSA加密最大块大小
        int offSet = 0;
        byte[] resultBytes = new byte[0];
        byte[] cache;

        // 分段加密
        while (inputLength - offSet > 0) {
            if (inputLength - offSet > MAX_ENCRYPT_BLOCK) {
                cache = cipher.doFinal(inputBytes, offSet, MAX_ENCRYPT_BLOCK);
                offSet += MAX_ENCRYPT_BLOCK;
            } else {
                cache = cipher.doFinal(inputBytes, offSet, inputLength - offSet);
                offSet = inputLength;
            }
            resultBytes = Arrays.copyOf(resultBytes, resultBytes.length + cache.length);
            System.arraycopy(cache, 0, resultBytes, resultBytes.length - cache.length, cache.length);
        }

        return java.util.Base64.getEncoder().encodeToString(resultBytes);
    }

    // ---------------------- 实体类定义 ----------------------

    /**
     * 接口请求参数实体
     */
    public static class SyncRequest {
        private String username;
        private String password;

        // Getters and Setters
        public String getUsername() { return username; }
        public void setUsername(String username) { this.username = username; }
        public String getPassword() { return password; }
        public void setPassword(String password) { this.password = password; }
    }

    /**
     * 同步结果实体
     */
    private static class SyncResult {
        private int totalCount;
        private boolean success;

        public SyncResult(int totalCount, boolean success) {
            this.totalCount = totalCount;
            this.success = success;
        }

        public int getTotalCount() { return totalCount; }
        public boolean isSuccess() { return success; }
    }

    /**
     * 统一API响应实体
     */
    public static class ApiResponse {
        private boolean success;
        private Object data;
        private String message;

        private ApiResponse(boolean success, Object data, String message) {
            this.success = success;
            this.data = data;
            this.message = message;
        }

        public static ApiResponse success(Object data) {
            return new ApiResponse(true, data, "操作成功");
        }

        public static ApiResponse error(String message) {
            return new ApiResponse(false, null, message);
        }

        // Getters
        public boolean isSuccess() { return success; }
        public Object getData() { return data; }
        public String getMessage() { return message; }
    }

    /**
     * Token请求实体
     */
    private static class TokenRequest {
        private final String grant_type = FIXED_GRANT_TYPE;
        private final String username;
        private final String password;
        private final String client_id = FIXED_CLIENT_ID;
        private final String client_secret = FIXED_CLIENT_SECRET;
        private final String public_key;

        public TokenRequest(String username, String encryptedPassword, String publicKey) {
            this.username = username;
            this.password = encryptedPassword;
            this.public_key = publicKey;
        }

        // Getters
        public String getGrant_type() { return grant_type; }
        public String getUsername() { return username; }
        public String getPassword() { return password; }
        public String getClient_id() { return client_id; }
        public String getClient_secret() { return client_secret; }
        public String getPublic_key() { return public_key; }
    }

    /**
     * Token响应实体
     */
    private static class TokenResponse {
        private boolean success;
        private TokenData data;
        private String code;
        private String errMsg;

        // Getters
        public boolean isSuccess() { return success; }
        public TokenData getData() { return data; }
        public String getCode() { return code; }
        public String getErrMsg() { return errMsg; }
    }

    /**
     * Token数据实体
     */
    private static class TokenData {
        private String access_token;
        private String refresh_token;
        private long expires_in;
        private String token_type;

        // Getters
        public String getAccess_token() { return access_token; }
        public long getExpires_in() { return expires_in; }
    }

    /**
     * 部门分页请求实体
     */
    private static class DepartmentPageRequest {
        private Integer pageNum;
        private Integer pageSize = FIXED_PAGE_SIZE;

        public DepartmentPageRequest(Integer pageNum) {
            this.pageNum = pageNum;
        }

        // Getters
        public Integer getPageNum() { return pageNum; }
        public Integer getPageSize() { return pageSize; }
    }

    /**
     * 部门分页响应实体
     */
    private static class DepartmentPageResponse {
        private boolean success;
        private DepartmentPageData data;
        private String code;
        private String errMsg;

        // Getters
        public boolean isSuccess() { return success; }
        public DepartmentPageData getData() { return data; }
        public String getCode() { return code; }
    }

    /**
     * 部门分页数据实体
     */
    private static class DepartmentPageData {
        private List<Department> pageData;
        private int totalPage;

        // Getters
        public List<Department> getPageData() { return pageData; }
        public int getTotalPage() { return totalPage; }
    }

    /**
     * 部门实体
     */
    private static class Department {
        private Long id;
        private Long parentId;
        private String name;
        private String memo;
        private Integer sort;
        private String parentIds;
        private Integer domainId;
        private String departmentSn;

        // Getters
        public Long getId() { return id; }
        public Long getParentId() { return parentId; }
        public String getName() { return name; }
        public String getMemo() { return memo; }
        public Integer getSort() { return sort; }
        public String getParentIds() { return parentIds; }
        public Integer getDomainId() { return domainId; }
        public String getDepartmentSn() { return departmentSn; }
    }

    /**
     * 公钥响应实体
     */
    private static class PublicKeyResponse {
        private boolean success;
        private PublicKeyData data;

        // Getters
        public boolean isSuccess() { return success; }
        public PublicKeyData getData() { return data; }
    }

    /**
     * 公钥数据实体
     */
    private static class PublicKeyData {
        private String publicKey;

        // Getters
        public String getPublicKey() { return publicKey; }
    }
}
